package com.lzugis.image;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.GeometryFactory;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.referencing.operation.MathTransform;
import scala.util.Random;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;

public class MapPrint {
    static int TILE_SIZE = 256;
    static double GLOBAL_EXTENT = 20037508.34;
    static double[] resolutions = new double[19];

    // 地图级别和出图范围
    static int ZOOM = 8;
    static double[] extent = {12225434.728643207, 2292518.1806185124, 12905418.532268135, 2865490.1446441934};
    static double interval[] = new double[]{0.5, 0.5};

//    static int ZOOM = 4;
//    static double[] extent = {7960094.725697456, 851859.8328115568, 15562215.810827946, 8219166.367049985};
//    static double interval[] = new double[]{10, 7.5};

    static int lonNum = (int) (180 / interval[0]);
    static int latNum = (int) (90 / interval[1]);
    static double[] lonInterval = new double[lonNum];
    static double[] latInterval = new double[latNum];

    static double[] lonlat = {114.10751997448422, 22.570026220694473};

    public static void main(String[] args) {
        long start = new Date().getTime();
        System.out.println("开始出图。。。");

        // 初始化分辨率组
        double res = (GLOBAL_EXTENT  * 2) / TILE_SIZE;
        for (int i = 0; i < 19; i++) {
            resolutions[i] = res;
            res = res / 2;
        }

        // 初始化经纬线
        for (int i = 1; i < lonNum; i++) {
            lonInterval[i] = interval[0] * i;
        }
        for (int i = 1; i < latNum; i++) {
            latInterval[i] = interval[1] * i;
        }

        // 获取切片的行列号
        int[] rowCol = getTileRowCol(extent);
        int xmin = rowCol[0];
        int xmax = rowCol[1];
        int ymin = rowCol[2];
        int ymax = rowCol[3];
        // 计算切片拼图的大小
        int width = (xmax - xmin) * TILE_SIZE;
        int height = (ymax - ymin) * TILE_SIZE;

        try {
            BufferedImage tileImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphicsTile = tileImage.createGraphics();
            for (int i = xmin; i <= xmax ; i++) {
                for (int j = ymin; j <= ymax ; j++) {
                    Random r = new Random();
                    int i1 = (int) (Math.random() * 4 + 1);
                    // http://t4.tianditu.com/DataServer?tk=c6a74bc1b752d5cfa699dbd386cafcf7&T=vec_w&x={x}&y={y}&l={z}
                    String destUrl = "http://webrd0"+i1+".is.autonavi.com/appmaptile?x="+i+"&y="+j+"&z="+ZOOM+"&lang=zh_cn&size=1&scale=1&style=8";
//                    String destUrl = "http://t"+i1+".tianditu.com/DataServer?tk=c6a74bc1b752d5cfa699dbd386cafcf7&T=vec_w&x="+i+"&y="+j+"&l="+ZOOM;
                    BufferedImage tile = getBufferedImageDestUrl(destUrl);
                    int x = (i - xmin) * TILE_SIZE;
                    int y = (j -ymin) * TILE_SIZE;
                    graphicsTile.drawImage(tile, x, y, TILE_SIZE, TILE_SIZE, null);
                }
            }

            // 添加边界图层
            double[] tileExtent = getTileExtent(xmin, xmax, ymin, ymax);
            String extentStr = tileExtent[0] + "," + tileExtent[1] + "," +tileExtent[2] + "," +tileExtent[3];
            String wmsUrl = "https://lzugis.cn/geoserver/lzugis/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&LAYERS=lzugis:cityboundry&CRS=EPSG%3A3857&STYLES=&WIDTH="+width+"&HEIGHT="+height+"&BBOX="+extentStr;
            BufferedImage wms = getBufferedImageDestUrl(wmsUrl);
            graphicsTile.drawImage(wms, 0, 0, width, height, null);

            // 绘制点
            Integer[] point = getPixel(wgs842webmactor(lonlat[0], lonlat[1]));
            Integer[] topLeftTile = getPixel(new double[]{tileExtent[0], tileExtent[3]});
            int x = point[0] - topLeftTile[0], y = point[1] - topLeftTile[1];
            graphicsTile.setColor(new Color(1, 84, 255));
            int size = 16;
            x = x - size / 2;
            y = y - size / 2;
            graphicsTile.fillOval(x, y, size, size);
            graphicsTile.dispose();

            // 计算出图范围的宽高
            Integer[] topLeft = getPixel(new double[]{extent[0], extent[3]}); //xmin, ymax
            Integer[] bottomRight = getPixel(new double[]{extent[2], extent[1]}); // xmax, ymin
            int widthE = (bottomRight[0] - topLeft[0]);
            int heightE = (bottomRight[1] - topLeft[1]);
            // 计算切片宽高和出图宽高的差值
            int deltX = (width - widthE) / 2;
            int deltY= (height - heightE) / 2;
            // 裁剪切片出图，获得出图图片
            BufferedImage extentImage = tileImage.getSubimage(deltX,deltY,widthE,heightE);

            // 向外扩30px，用于展示经纬线和标记
            int offset = 30;
            int widthM = widthE + offset * 2;
            int heightM = heightE + offset * 2;
            BufferedImage mergeImage = new BufferedImage(widthM, heightM, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphicsMerge = mergeImage.createGraphics();
            graphicsMerge.setColor(Color.WHITE);//设置笔刷白色
            graphicsMerge.fillRect(0,0,widthM,heightM);//填充整个屏幕
            // 绘制出图地图
            graphicsMerge.drawImage(extentImage, offset, offset, widthE, heightE, null);
//            generateSaveFile(mergeImage, "merge1.png");

            // 获取经纬度
            double[] minLonLat = webmactor2wgs84(extent[0], extent[1]);
            double[] maxLonLat = webmactor2wgs84(extent[2], extent[3]);
            int lonIndexMin = (int) Math.floor(minLonLat[0] / interval[0]);
            int lonIndexMax = (int) Math.ceil(maxLonLat[0] / interval[0]);
            int latIndexMin = (int) Math.floor(minLonLat[1] / interval[1]);
            int latIndexMax = (int) Math.ceil(maxLonLat[1] / interval[1]);

            double latMin= minLonLat[1], lonMin = minLonLat[0], latMax = maxLonLat[1], lonMax = maxLonLat[0];
            Stroke solid = new BasicStroke(1);
            graphicsMerge.setStroke(solid);
            // 设置字体
            Font font=new Font("Times New Roman",Font.PLAIN,14);
            graphicsMerge.setFont(font);
            // 抗锯齿
            graphicsMerge.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            FontMetrics fm = graphicsMerge.getFontMetrics(font);

            for (int i = lonIndexMin; i <= lonIndexMax ; i++) {
                String lon = String.valueOf((Double.valueOf(lonInterval[i] * 100).intValue()) / 100.0);
                double[] coords = wgs842webmactor(lonInterval[i], latMin);
                Integer[] pixel = getPixel(coords);
                // x1, int y1, int x2, int y2
                int x1 = pixel[0] - topLeftTile[0] + offset - deltX;
                int y1 = offset, y2 = heightE + offset;

                if(x1 > offset && x1 < widthE + offset){
                    // 绘制竖直的经线和经度标注
                    graphicsMerge.setColor(new Color(0, 144, 255));
                    graphicsMerge.drawLine(x1, y1, x1, y2);

                    // 计算文字长度，计算居中的x点坐标
                    int textWidth = fm.stringWidth(lon) / 2;
                    graphicsMerge.setColor(Color.BLACK);
                    graphicsMerge.drawString(lon, x1 - textWidth, y1 - 10); // 上面的文字
                    graphicsMerge.drawString(lon, x1 - textWidth, y2 + 20); // 下面的文字
                }
            }
            // 旋转文字
            AffineTransform affineTransform = new AffineTransform();
            affineTransform.rotate(Math.toRadians(-90), 0, 0);
            Font rotatedFont = font.deriveFont(affineTransform);
            graphicsMerge.setFont(rotatedFont);
            for (int i = latIndexMin; i <= latIndexMax ; i++) {
                String lat = String.valueOf((Double.valueOf(latInterval[i] * 100).intValue()) / 100.0);
                double[] coords = wgs842webmactor(lonMin, latInterval[i]);
                Integer[] pixel = getPixel(coords);
                // x1, int y1, int x2, int y2
                int y1 = pixel[1] - topLeftTile[1] + offset - deltY;
                int x1 = offset, x2 = widthE + offset;

                if(y1 > offset && y1 < heightE + offset) {
                    // 绘制竖直的经线
                    graphicsMerge.setColor(new Color(0, 144, 255));
                    graphicsMerge.drawLine(x1, y1, x2, y1);

                    // 计算文字长度，计算居中的x点坐标
                    int textWidth = fm.stringWidth(lat) / 2;
                    graphicsMerge.setColor(Color.BLACK);
                    graphicsMerge.drawString(lat, x1 - 10, y1 + textWidth); // 左面的文字
                    graphicsMerge.drawString(lat, x2 + 20, y1 + textWidth); // 右面的文字
                }
            }
            // 保存图片
            graphicsMerge.dispose();
            generateSaveFile(mergeImage, "merge2.png");

            long end = new Date().getTime();
            System.out.println(String.format("出图结束，耗时%sms", end - start));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 坐标转化
     * @param x
     * @param y
     * @return
     */
    public static double[] webmactor2wgs84(double x, double y){
        try{
            GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
            MathTransform transform = CRS.findMathTransform(CRS.decode("EPSG:3857"), DefaultGeographicCRS.WGS84);
            Coordinate coordinate = new Coordinate(x, y);
            Geometry geom = geometryFactory.createPoint(coordinate);
            Point dist = (Point) JTS.transform(geom, transform);
            return new double[] {dist.getX(), dist.getY()};
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return new double[] {-99, -99};
        }
    }

    public static double[] wgs842webmactor(double lon, double lat){
        try{
            GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
            MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, CRS.decode("EPSG:3857"));
            Coordinate coordinate = new Coordinate(lon, lat);
            Geometry geom = geometryFactory.createPoint(coordinate);
            Point dist = (Point) JTS.transform(geom, transform);
            return new double[] {dist.getX(), dist.getY()};
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return new double[] {-99, -99};
        }
    }

    /**
     * 获取切片的坐标范围
     * @param xmin
     * @param xmax
     * @param ymin
     * @param ymax
     * @return
     */
    public static double[] getTileExtent(Integer xmin, Integer xmax, Integer ymin, Integer ymax) {
        double res = resolutions[ZOOM] * TILE_SIZE;
        double originY = GLOBAL_EXTENT;
        double originX = -GLOBAL_EXTENT;
        double xminE = originX + (xmin * res);
        double xmaxE = originX + (xmax * res);
        double yminE = originY - (ymax * res);
        double ymaxE = originY - (ymin * res);
        return new double[] {xminE, yminE, xmaxE, ymaxE};
    }

    /**
     * 根据地图级别计算屏幕坐标
     * @param coords
     * @return
     */
    public static Integer[] getPixel(double[] coords) {
        double res = resolutions[ZOOM];
        double originY = GLOBAL_EXTENT;
        double originX = -GLOBAL_EXTENT;
        double coordX = coords[0], coordY = coords[1];
        Integer x = (int) ((coordX - originX) / res);
        Integer y = (int) ((originY - coordY) / res);
        return new Integer[]{x, y};
    }

    /**
     * 根据范围获取切片行列号
     * @param {double[]} extent - 出图四至，格式为[xmin, ymin, xmax, ymax]
     * @return {int[]} - 切片的行列号范围
     */
    public static int[] getTileRowCol (double[] extent) {
        if(extent.length != 4) return new int[]{};
        double originX = -GLOBAL_EXTENT;
        double originY = GLOBAL_EXTENT;
        double res = resolutions[ZOOM] * TILE_SIZE;
        int xmin = (int) Math.floor((extent[0] - originX) / res);
        int xmax = (int) Math.ceil((extent[2] - originX) / res);
        int ymax = (int) Math.ceil((originY - extent[1]) / res);
        int ymin = (int) Math.floor((originY - extent[3]) / res);
        return new int[]{ xmin, xmax, ymin, ymax};
    }

    /**
     * 远程图片转BufferedImage
     * @return {BufferedImage} - 返回图片BufferedImage
     */
    public static BufferedImage getBufferedImageDestUrl(String destUrl) {
        HttpURLConnection conn = null;
        BufferedImage image = null;
        try {
            URL url = new URL(destUrl);
            conn = (HttpURLConnection) url.openConnection();
            if (conn.getResponseCode() == 200) {
                image = ImageIO.read(conn.getInputStream());
                return image;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conn.disconnect();
        }
        return image;
    }

    /**
     * 输出图片
     * @param buffImg  图像拼接叠加之后的BufferedImage对象
     * @param savePath 图像拼接叠加之后的保存路径
     */
    public static void generateSaveFile(BufferedImage buffImg, String savePath) {
        int temp = savePath.lastIndexOf(".") + 1;
        try {
            File outFile = new File(savePath);
            if(!outFile.exists()){
                outFile.createNewFile();
            }
            ImageIO.write(buffImg, savePath.substring(temp), outFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
